package com.hk.core.autoconfigure.data;

import com.hk.commons.util.Constants;
import com.hk.core.authentication.api.SecurityContext;
import com.hk.core.authentication.api.UserPrincipal;
import com.hk.core.authentication.api.reactive.ReactiveSecurityContext;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.ReactiveAuditorAware;
import reactor.core.publisher.Mono;

import java.util.Optional;

/**
 * Jpa、JDBC auditor
 *
 * @author Kevin
 * @date 2018-10-10 10:11
 */
@AutoConfiguration
public class AuditorConfiguration {

    /**
     * Servlet 配置
     */
    @ConditionalOnClass(value = {AuditorAware.class, SecurityContext.class})
    @ConditionalOnMissingBean(value = AuditorAware.class)
    @ConditionalOnBean(value = SecurityContext.class)
    static class DefaultAuditorConfiguration {

        @Bean
        public AuditorAware<Long> auditorAware(SecurityContext securityContext) {
            return new UserAuditorAware(securityContext);
        }

        private record UserAuditorAware(SecurityContext securityContext) implements AuditorAware<Long> {

            @Override
            public Optional<Long> getCurrentAuditor() {
                return Optional.of(securityContext.isAuthenticated() ?
                        securityContext.getPrincipal().getUserId() : Constants.DEFAULT_VALUE_LONG);
            }
        }
    }

    /**
     * Reactive 配置，如 r2dbc
     */
    @ConditionalOnClass(value = {ReactiveAuditorAware.class, ReactiveSecurityContext.class})
    @ConditionalOnMissingBean(value = ReactiveAuditorAware.class)
    @ConditionalOnBean(value = ReactiveSecurityContext.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    static class ReactiveAuditorConfiguration {
        @Bean
        public ReactiveAuditorAware<Long> reactiveAuditorAware(ReactiveSecurityContext reactiveSecurityContext) {
            return new UserReactiveAuditorAware(reactiveSecurityContext);
        }

        private record UserReactiveAuditorAware(
                ReactiveSecurityContext reactiveSecurityContext) implements ReactiveAuditorAware<Long> {

            @Override
            public Mono<Long> getCurrentAuditor() {
                return reactiveSecurityContext.isAuthenticated()
                        .switchIfEmpty(Mono.just(Boolean.FALSE))
                        .flatMap(authenticated -> authenticated
                                ? reactiveSecurityContext.getPrincipal().map(UserPrincipal::getUserId)
                                : Mono.just(Constants.DEFAULT_VALUE_LONG));
            }
        }
    }

}
